package com.cloud.common.sentinel;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.cloud.common.support.BaseInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Slf4j
@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
@ConditionalOnMissingClass("com.cloud.gateway.bean.GatewayInfo")
public class SentinelInterceptor extends BaseInterceptor {

    @Around("target(com.baomidou.mybatisplus.extension.service.IService)")
    public Object doService(ProceedingJoinPoint point) throws Throwable {
        return handle(point, EntryType.OUT);
    }

    @Around("target(com.baomidou.mybatisplus.core.mapper.BaseMapper)")
    public Object doMapper(ProceedingJoinPoint point) throws Throwable {
        return handle(point, EntryType.OUT);
    }

    @Around("@within(org.springframework.web.bind.annotation.RestController)")
    public Object doController(ProceedingJoinPoint point) throws Throwable {
        return handle(point, EntryType.IN);
    }

    @Around("@within(Sentinel)")
    public Object doSentinel(ProceedingJoinPoint point) throws Throwable {
        return handle(point, EntryType.OUT);
    }

    private Object handle(ProceedingJoinPoint point, EntryType entryType) throws Throwable {
        String resourceName = getResourceName(point);
        Entry entry = null;
        try {
            // Sentinel检查
            entry = SphU.entry(resourceName, entryType);
            return execute(point, null);
        } catch (Throwable e) {
            Tracer.traceEntry(e, entry);
            if (BlockException.isBlockException(e)) {
                log.warn("{} execute break", resourceName);
            }
            throw e;
        } finally {
            // Sentinel清理
            if (entry != null) {
                entry.exit();
            }
        }
    }

    private String getResourceName(ProceedingJoinPoint point) {
        // 获取类名
        String className;
        Class<?> classTarget = point.getTarget().getClass();
        Class<?>[] interfaces = classTarget.getInterfaces();
        if (interfaces.length != 0) {
            className = interfaces[0].getSimpleName();
        } else {
            className = classTarget.getSimpleName();
        }

        // 获取方法名
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        String methodName = methodSignature.getMethod().getName();
        return "/" + className + "/" + methodName;
    }
}
